package cn.turboinfo.fuyang.api.domain.util;

import com.google.common.io.BaseEncoding;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * 安全工具集合类
 *
 * @author sunshow
 */
public abstract class SecurityUtils {

    public static final String RSA_ECB_PKCS1PADDING = "RSA/ECB/PKCS1PADDING";

    public static final String AES_CBC_PKCS7PADDING = "AES/CBC/PKCS7PADDING";

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * base64编码
     *
     * @param input 输入数据
     * @return 编码结果
     */
    public static String encodeBase64(byte[] input) {
        return BaseEncoding.base64().encode(input);
    }

    /**
     * base64解码
     *
     * @param encodeString 已编码字符串
     * @return 解码结果
     */
    public static byte[] decodeBase64(String encodeString) {
        return BaseEncoding.base64().decode(encodeString);
    }

    /**
     * 十六进制编码
     *
     * @param input 输入数据
     * @return 编码结果 小写字母
     */
    public static String encodeHex(byte[] input) {
        return BaseEncoding.base16().lowerCase().encode(input);
    }

    /**
     * 十六进制解码
     *
     * @param encodeString 已编码字符串 小写字母
     * @return 解码结果
     */
    public static byte[] decodeHex(String encodeString) {
        return BaseEncoding.base16().lowerCase().decode(encodeString);
    }

    /**
     * RSA用私钥对信息生成数字签名
     *
     * @param input             源数据
     * @param encodedPrivateKey 私钥
     * @return 签名结果
     */
    public static byte[] signRSA(byte[] input, byte[] encodedPrivateKey) {
        try {
            //构造PKCS8EncodedKeySpec对象
            var pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);
            //指定加密算法
            var keyFactory = KeyFactory.getInstance("RSA");
            //取私钥对象
            var privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
            //用私钥对信息生成数字签名
            var signature = Signature.getInstance("SHA256WithRSA");
            signature.initSign(privateKey);
            signature.update(input);
            return signature.sign();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * RSA用公钥验证数字签名
     *
     * @param input            源数据
     * @param encodedPublicKey 公钥
     * @param sign             签名
     * @return 签名结果
     */
    public static boolean verifySignRSA(byte[] input, byte[] encodedPublicKey, byte[] sign) {
        try {
            //构造X509EncodedKeySpec对象
            var x509EncodedKeySpec = new X509EncodedKeySpec(encodedPublicKey);
            //指定加密算法
            var keyFactory = KeyFactory.getInstance("RSA");
            //取公钥匙对象
            var publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
            //用公钥对信息生成数字签名
            var signature = Signature.getInstance("SHA256WithRSA");
            signature.initVerify(publicKey);
            signature.update(input);
            //验证签名是否正常
            return signature.verify(sign);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * RSA公钥加密
     *
     * @param input            源数据
     * @param encodedPublicKey 公钥
     * @return 加密后的结果
     */
    public static byte[] encryptRSA(byte[] input, byte[] encodedPublicKey) {
        try {
            //构造X509EncodedKeySpec对象
            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(encodedPublicKey);
            //指定加密算法
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            //取公钥对象
            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
            //加密
            Cipher cipher = Cipher.getInstance(RSA_ECB_PKCS1PADDING);// 不能直接使用RSA，要写全转换模式，不然与android不一致
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            return cipher.doFinal(input);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * RSA私钥解密
     *
     * @param input            源数据
     * @param encodePrivateKey 私钥
     * @return 解密后的结果
     */
    public static byte[] decryptRSA(byte[] input, byte[] encodePrivateKey) {
        try {
            //构造PKCS8EncodedKeySpec对象
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(encodePrivateKey);
            //指定加密算法
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            //取私钥对象
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
            //解密
            Cipher cipher = Cipher.getInstance(RSA_ECB_PKCS1PADDING); // 不能直接使用RSA，要写全转换模式，不然与android不一致
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return cipher.doFinal(input);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * aes加密
     * CBC模式，需要128位初始化向量
     *
     * @param input      源数据
     * @param encodedKey 私钥
     * @param iv         16字节初始化向量
     * @return 加密后的结果
     */
    public static byte[] encryptAES(byte[] input, byte[] encodedKey, byte[] iv) {
        try {
            //构造SecretKeySpec对象
            SecretKeySpec secretKeySpec = new SecretKeySpec(encodedKey, "AES");
            // 初始化向量
            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
            //加密
            Cipher cipher = Cipher.getInstance(AES_CBC_PKCS7PADDING, "BC");
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);

            return cipher.doFinal(input);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * AES解密
     * CBC模式，需要128位初始化向量
     *
     * @param input      源数据
     * @param encodedKey 私钥
     * @param iv         16字节初始化向量，与加密时保持一致
     * @return 解密后的结果
     */
    public static byte[] decryptAES(byte[] input, byte[] encodedKey, byte[] iv) {
        try {
            //构造SecretKeySpec对象
            SecretKeySpec secretKeySpec = new SecretKeySpec(encodedKey, "AES");
            // 初始化向量
            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
            //解密
            Cipher cipher = Cipher.getInstance(AES_CBC_PKCS7PADDING, "BC");
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
            return cipher.doFinal(input);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 生成随机数
     *
     * @return 随机数
     */
    public static String generateAppId(Long companyId) {
        // 生成随机数
        var random = new SecureRandom();
        var bytes = new byte[16];
        random.nextBytes(bytes);
        // 生成appId
        var appId = BaseEncoding.base16().lowerCase().encode(bytes);
        return String.format("%s%s", companyId, appId);
    }

    public static String generateAppSecret() {
        // 生成随机数
        var random = new SecureRandom();
        var bytes = new byte[32];
        random.nextBytes(bytes);
        // 生成appSecret
        return BaseEncoding.base16().lowerCase().encode(bytes);
    }
}
